package de.bastian.hinterleitner;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import de.bastian.hinterleitner.database.DataHelper;
import de.bastian.hinterleitner.tools.Category;
import de.bastian.hinterleitner.tools.Constants;
import de.bastian.hinterleitner.tools.DBCopyHelper;
import de.bastian.hinterleitner.tools.Entry;
import de.bastian.hinterleitner.tools.ErrorLog;
import de.bastian.hinterleitner.tools.WatchYourCashActivity;
import de.bastian.hinterleitner.tools.XmlHelper;
import de.hijacksoft.wyc.R;

public class XmlActivity extends WatchYourCashActivity implements
		OnClickListener {

	private static final String TAG = "WatchYourCash_XmlActivity";
	
	private static final String PASSWORD = "12efa3708ed86c5ebaf7af8dbe16262d";
	private static final String USERNAME = "basti";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_xml);
		((Button) findViewById(R.id.xml_export)).setOnClickListener(this);
		((Button) findViewById(R.id.xml_import)).setOnClickListener(this);
		((Button) findViewById(R.id.web_export)).setOnClickListener(this);
		initNav((LinearLayout) findViewById(R.id.xml_header),
				getString(R.string.exportimport));
		initFooter(((LinearLayout) findViewById(R.id.xml_layout_footer)));
		initLeftNavigation(((LinearLayout) findViewById(R.id.main_layout_overlay)));
	}

	@Override
	public void onClick(View v) {
		Intent intent;
		switch (v.getId()) {
		case R.id.xml_export:
			exportDatabase();
			break;
		case R.id.xml_import:
			importDatabase();
			break;
		case R.id.web_export:
			webExportDatabase();
			break;
		}
	}

	private void webExportDatabase() {
		new WebTask().execute();
	}

	private void importDatabase() {
		Intent intent;
		intent = new Intent(Intent.ACTION_GET_CONTENT);
		intent.setType("file/*.xml");
		try {
			startActivityForResult(intent, Constants.REQUEST_IMPORT_FILE);
		} catch (ActivityNotFoundException ex) {
			dialog(getString(R.string.error), getString(R.string.nofilechooser));
		} catch (java.lang.SecurityException ex){
			dialog(getString(R.string.error), getString(R.string.permission));
		}
	}

	private void exportDatabase() {
		final String path = Environment.getExternalStorageDirectory()
				+ "/hijacksoft/watchyourcash/" + "watchyourcash_"
				+ getFormattedDate() + ".db";

		new AsyncHelper(this, path.endsWith(".db") ? path : (path + ".db"))
				.execute(new Command<Boolean>() {
					@Override
					public int type() {
						return TYPE_EXPORT;
					}

					@Override
					public Boolean execute() {
						return DBCopyHelper.exportDatabase(path,
								getApplicationContext());
					}
				});
	}

	private String getFormattedDate() {
		Calendar c = Calendar.getInstance();
		String result = c.get(Calendar.YEAR) + "-";
		result += addDecimalNulls((c.get(Calendar.MONTH) + 1), 2) + "-";
		result += addDecimalNulls(c.get(Calendar.DAY_OF_MONTH), 2) + "_";
		result += addDecimalNulls(c.get(Calendar.HOUR_OF_DAY), 2) + "-";
		result += addDecimalNulls(c.get(Calendar.MINUTE), 2) + "-";
		result += addDecimalNulls(c.get(Calendar.SECOND), 2);
		return result;
	}

	private String addDecimalNulls(int src, int targetLength) {
		String result = "" + src;
		while (result.length() < targetLength) {
			result = "0" + result;
		}
		return result;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (resultCode == RESULT_OK && data != null) {
			final String path = data.getData().getPath();
			switch (requestCode) {
			case Constants.REQUEST_IMPORT_FILE:
				new AsyncHelper(this, path).execute(new Command<Boolean>() {
					@Override
					public int type() {
						return TYPE_IMPORT;
					}

					@Override
					public Boolean execute() {
						return DBCopyHelper.importDatabase(path,
								getApplicationContext());
					}
				});
				break;
			}
		}
	}

	@SuppressWarnings("rawtypes")
	private class AsyncHelper extends
			AsyncTask<Command, Command<Integer>, String> {

		private ProgressDialog pd;
		private final Context context;
		private UpdateHelper uh;
		private String exportpath;

		public AsyncHelper(Context c, String path) {
			context = c;
			exportpath = path;
		}

		@Override
		protected void onPreExecute() {
			pd = new ProgressDialog(context);
			pd.setMessage(context.getString(R.string.updateingdatabase));
			pd.setCancelable(false);
			pd.show();
		}

		@Override
		protected void onProgressUpdate(Command<Integer>... values) {
			uh = new UpdateHelper(context, pd, values[0]);
			if (values[0].type() == Command.TYPE_EXPORT) {
				uh.maximum = DataHelper.getDataHelper(context).getNumEntries();
			} else {
				uh.maximum = values[0].type();
			}
			Log.d(TAG, "maximum=" + uh.maximum);
			uh.execute();
		}

		@SuppressWarnings("unchecked")
		@Override
		protected String doInBackground(Command... params) {

			// error if no parameters were passed!
			if (params.length < 1) {
				ErrorLog.write(XmlActivity.this,
						"no arguments passed for AsyncTask (XmlAcitivity:184)");
				return context.getString(R.string.error) + "\n"
						+ context.getString(R.string.sthwentwrong);
			}

			// show an export progress
			if (params[0].type() == Command.TYPE_EXPORT) {
				publishProgress(new Command<Integer>() {
					@Override
					public int type() {
						return TYPE_EXPORT;
					}

					@Override
					public Integer execute() {
						return XmlHelper.status;
					}
				});
			}

			final Object result = params[0].execute();

			// load into database if its an arraylist of entries
			if (params[0].type() == Command.TYPE_IMPORT) {
				try {
					publishProgress(new Command<Integer>() {
						@Override
						public int type() {
							return TYPE_IMPORT;
						}

						@Override
						public Integer execute() {
							return XmlHelper.status;
						}
					});
				} catch (ClassCastException e) {
					ErrorLog.write(context, e);
					// error if the arraylist doesnt contain entries
					return context.getString(R.string.error) + "\n"
							+ context.getString(R.string.sthwentwrong);
				}
				// importing went well
				return context.getString(R.string.importsuccess);
			} else
			// see if exporting worked
			if (result instanceof Boolean) {
				if ((Boolean) result) {
					// did work
					return context.getString(R.string.exportsuccess).replace(
							"$1", exportpath);
				} else {
					// did not work
					return context.getString(R.string.error) + "\n"
							+ context.getString(R.string.couldntexport);
				}
			} else {
				// give an error if its neither an arraylist nor a boolean ->
				// importing returned null
				return context.getString(R.string.error)
						+ "\n"
						+ context.getString(R.string.couldntimport).replace(
								"$1", exportpath);
			}
		}

		@Override
		protected void onPostExecute(String result) {
			pd.dismiss();
			startActivity(new Intent(getApplicationContext(),
					LoadingActivity.class));
			dialog(result);
		}

	}

	private class UpdateHelper extends AsyncTask<Object, Integer, Object> {

		public int maximum = -1;
		private int status;
		private ProgressDialog pd;
		private Command<Integer> command;

		public UpdateHelper(Context c, ProgressDialog pd,
				Command<Integer> command) {
			this.pd = pd;
			this.command = command;
		}

		@Override
		protected void onProgressUpdate(Integer... values) {
			if (maximum > 0 && values.length > 0) {
				status = values[0];
				pd.setMax(maximum);
				pd.setProgress(status);
			}
		}

		@Override
		protected Object doInBackground(Object... params) {
			while ((status < maximum || maximum == -1) && pd.isShowing()) {
				try {
					Thread.sleep(50);
					if (maximum > 0) {
						int result = command.execute();
						Log.d(TAG, "result=" + result);
						publishProgress(result);
					}
				} catch (InterruptedException e) {
					ErrorLog.write(XmlActivity.this, e);
				}
			}
			return null;
		}

	}

	private abstract class Command<E> {
		public static final int TYPE_NORMAL = 0;
		public static final int TYPE_IMPORT = 1;
		public static final int TYPE_EXPORT = 2;

		public int type() {
			return TYPE_NORMAL;
		}

		public abstract E execute();
	}

	@Override
	public boolean hasOptionsMenu() {
		return false;
	}

	@SuppressWarnings("rawtypes")
	private class WebTask extends AsyncTask {

		private ProgressDialog pd;
		boolean running = true;
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pd = new ProgressDialog(XmlActivity.this);
			pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			int max = DataHelper.getDataHelper(XmlActivity.this).getNumEntries()+DataHelper.getDataHelper(XmlActivity.this).getAllCategories().size();
			pd.setMax(max);
			pd.setMessage(getString(R.string.exportimport));
			pd.setOnCancelListener(new OnCancelListener() {
				@Override
				public void onCancel(DialogInterface dialog) {
					running = false;
				}
			});
			pd.show();
		}
		
		@SuppressWarnings("unchecked")
		@Override
		protected void onProgressUpdate(Object... values) {
			super.onProgressUpdate(values);
			pd.setProgress(pd.getProgress()+1);
		}
		
		@SuppressWarnings("unchecked")
		@Override
		protected Object doInBackground(Object... arg0) {
			ArrayList<Category> cats = DataHelper.getDataHelper(XmlActivity.this).getAllCategories();
			for(int i=0; i<cats.size(); i++){
				if(!running){
					return null;
				}
				send(cats.get(i));
				publishProgress();
			}
			ArrayList<Entry> list = DataHelper.getDataHelper(XmlActivity.this).getAllEntries();
			for(int i=0; i<list.size(); i++){
				if(!running){
					return null;
				}
				send(list.get(i));
				publishProgress();
			}
			return null;
		}

		protected void onPostExecute(Object result) {
			pd.dismiss();
		}
		
	}
	
	
	
	private void send(Entry entry) {
		long date = new GregorianCalendar(entry.getYear(), entry.getMonth()-1, entry.getDay()).getTimeInMillis();
		String query = "http://basti.hijacksoft.de/wyc/insert.php?";
		HashMap<String,String> map = new HashMap<String, String>();
		map.put("username", USERNAME);
		map.put("password", PASSWORD);
		map.put("name", entry.getName());
		map.put("value", entry.getValue()+"");
		map.put("currency", entry.getUnit().getSign());
		map.put("category", entry.getCategory().id+"");
		map.put("id", entry.getId()+"");
		map.put("date", date+"");
		query += urlGet(map);
		Log.d(TAG, query);
		try {
			new URL(query).openStream();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void send(Category cat) {
		String query = "http://basti.hijacksoft.de/wyc/insert.php?";
		HashMap<String,String> map = new HashMap<String, String>();
		map.put("username", USERNAME);
		map.put("password", PASSWORD);
		map.put("type", "category");
		map.put("name", cat.name);
		map.put("id", cat.id+"");
		map.put("icon", cat.icon+".png");
		query += urlGet(map);
		Log.d(TAG, query);
		try {
			new URL(query).openStream();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private String urlGet(HashMap<String, String> map) {
		String result = "";
		for(String s : map.keySet()){
			try {
				result += s+"="+ URLEncoder.encode(map.get(s), "ISO-8859-1")+"&";
			} catch (UnsupportedEncodingException e) {
				result += s+"="+ URLEncoder.encode(map.get(s))+"&";
			}
		}
		return result.substring(0, result.length()-1);
	}

}
